package cn.blnp.net.flowable.event.middle.variable;

import cn.hutool.core.map.MapUtil;
import lombok.extern.slf4j.Slf4j;
import org.flowable.engine.HistoryService;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.TaskService;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.repository.Deployment;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.api.Task;
import org.flowable.task.api.TaskQuery;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

import javax.annotation.Resource;
import java.util.HashMap;
import java.util.Map;

/**
 * <h3>中间事件-变量监听器中间捕获事件</h3>
 *
 * @author <a href="mailto:blnp.yibin@qq.com">lyb</a>
 * @version 1.0
 * @since 2025/5/12 9:46
 */
@Slf4j
@SpringBootTest
public class MidVariableEvent {

    @Resource
    private RepositoryService repositoryService;
    @Resource
    private RuntimeService runtimeService;
    @Resource
    private TaskService taskService;
    @Resource
    private HistoryService historyService;

    /**
     *  <p>
     *      变量监听器中间捕获事件指在流程中将一个变量监听器事件作为独立的节点来运行，是一种捕获事件。当流程执行到变量监听器中间捕获事件时会暂停，等待指定的变量变化事件触发后，
     *  流程沿外出顺序流继续流转。具体事件定义格式如下：
     *  <pre>
     *      {@code
     *          <intermediateCatchEvent id="variableListenerIntermediateCatchEvent1">
     *              <!-- 包含variableListenerEventDefinition子元素，代表变量监听器中间捕获事件 -->
     *              <extensionElements>
     *                  <flowable:variableListenerEventDefinition variableName="materialState" variableChangeType="all"/>
     *              </extensionElements>
     *          </intermediateCatchEvent>
     *      }
     *  </pre>
     *  </p>
     *  <h2>案例演示</h2>
     *  <hr/>
     *  <p>
     *      该流程为Offer申请流程：Offer申请提交后，首先到达“材料初审”任务节点，如果判断材料没有问题，则流程流转到变量监听器中间捕获事件；如果判断材料存在问题，则流程流转到“材料复审”任务
     *  节点。材料复审完成时流转到变量监听器中间捕获事件，同时触发变量监听事件发起“外部背调事件子流程”，流转到“背调”任务节点。背调完成时更新变量值。<br/>
     *  <b>流程定义文件：</b>{@link flow/midVariableEvent.bpmn20.xml}<br/>
     *  <b>流程预览图：</b><br/>
     *  <img src="https://cdn4.winhlb.com/2025/05/12/68215858385c0.png" />
     *  </p>
     **/
    @Test
    public void test() {
        /**
         *  流程解析：
         *  1、变量监听器边界事件boundaryEvent1的扩展元素中通过variableListenerEventDefinition子元素配置variableName属性值为materialState、variableChangeType属性值为update，
         *  说明当变量materialState发生值变化时将触发该边界事件沿着外出顺序流继续流转。该边界事件的cancelActivity值配置为true，说明它是一个中断事件，事件触发时依附的用户任务将被终止。
         *
         *  2、变量监听器中间捕获事件intermediateCatchEvent1的扩展元素中通过variableListenerEventDefinition子元素配置variableName属性值为materialState、variableChangeType属性值为all，
         *  说明变量materialState的所有create、update和delete事件都将触发离开该中间事件。
         *
         *  3、变量监听器开始事件startEvent2的扩展元素中通过variableListenerEventDefinition子元素配置variableName属性值为backgroundCheckState、variableChangeType属性值为create，
         *  说明当变量backgroundCheckState创建时将触发该事件子流程发起。该变量监听器开始事件的isInterrupting值配置为false，说明它是一个非中断事件。
         **/
        Deployment deploy = repositoryService.createDeployment()
                .name("变量中间捕获事件案例")
                .addClasspathResource("flow/midVariableEvent.bpmn20.xml")
                .deploy();
        log.info("部署流程定义：{}", deploy.getId());
        ProcessDefinition procDef = repositoryService.createProcessDefinitionQuery()
                .deploymentId(deploy.getId())
                .singleResult();
        log.info("流程定义编号：{}", procDef.getId());
        //设置变量
        Map<String, Object> varMap1 = MapUtil.builder(new HashMap<String, Object>())
                .put("materialState", "init")
                .build();
        //启动流程实例
        ProcessInstance procInst = runtimeService.startProcessInstanceById(procDef.getId(), varMap1);
        TaskQuery taskQuery = taskService.createTaskQuery()
                .processInstanceId(procInst.getId());
        //查询并办理第一个任务
        Task firstTask = taskQuery.singleResult();
        taskService.complete(firstTask.getId());
        Task secondTask = taskQuery.singleResult();
        log.info("流程流转到：{}", secondTask.getName());

        //修改变量
        runtimeService.setVariable(procInst.getId(), "materialState", "fushen");
        Task thirdTask = taskQuery.singleResult();
        log.info("修改变量materialState值为fushen后，流程流转到：{}", thirdTask.getName());
        //完成第三个任务同时修改变量
        Map<String, Object> varMap2 = MapUtil.builder(new HashMap<String, Object>())
                .put("backgroundCheckState", "init")
                .build();
        taskService.complete(thirdTask.getId(), varMap2);
        Task fourthTask = taskQuery.singleResult();
        log.info("新建变量backgroundCheckState后，流程流转到：{}", fourthTask.getName());
        //完成第四个任务同时修改变量
        Map<String, Object> varMap3 = MapUtil.builder(new HashMap<String, Object>())
                .put("materialState", "success")
                .build();
        taskService.complete(fourthTask.getId(), varMap3);
        HistoricProcessInstance historicProcessInstance = historyService
                .createHistoricProcessInstanceQuery()
                .processInstanceId(procInst.getId()).singleResult();
        if (historicProcessInstance.getEndTime() != null) {
            log.info("修改变量materialState值为success后，流程已结束");
        }

        /**
         *  演示结果：
         * 2025-05-12 10:20:30.377  INFO 97560 --- [           main] c.b.n.f.e.m.variable.MidVariableEvent    : 部署流程定义：ac916026-2ed7-11f0-9a4b-50ebf6ec8f6d
         * 2025-05-12 10:20:30.380  INFO 97560 --- [           main] c.b.n.f.e.m.variable.MidVariableEvent    : 流程定义编号：midVariableEvent:1:acfca519-2ed7-11f0-9a4b-50ebf6ec8f6d
         * 2025-05-12 10:20:30.566  INFO 97560 --- [           main] c.b.n.f.e.m.variable.MidVariableEvent    : 流程流转到：材料初审
         * 2025-05-12 10:20:30.626  INFO 97560 --- [           main] c.b.n.f.e.m.variable.MidVariableEvent    : 修改变量materialState值为fushen后，流程流转到：材料复审
         * 2025-05-12 10:20:30.651  INFO 97560 --- [           main] c.b.n.f.e.m.variable.MidVariableEvent    : 新建变量backgroundCheckState后，流程流转到：背调
         * 2025-05-12 10:20:30.710  INFO 97560 --- [           main] c.b.n.f.e.m.variable.MidVariableEvent    : 修改变量materialState值为success后，流程已结束
         **/
    }
}
